iT邦幫忙

2022 iThome 鐵人賽

DAY 14
0
自我挑戰組

30天學習flutter系列 第 14

14.flutter的狀態管理(二)

  • 分享至 

  • xImage
  •  

狀態管理:setState

在flutter的預設程式碼中,展示了setState如何使用

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Device Calendar',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}
class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  // Here
  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), 
    );
  }
}

當user與widget進行交互,並且widget發生變化,那麼它就是Stateful State,並且無狀態的 widget 自身無法改變。

並且前面提到過一個具Stateful的widget,他的狀態保存在一個State對像中,它和 widget的顯示分離。當widget狀態改變時,State對象調用setState(),告訴框架(聲明式)去重繪我們的widget

那麼,setState是如何實現更widget的呢?

我們看到flutter\lib\src\widgets\framework.dart裡的State類裡的setState()方法
https://ithelp.ithome.com.tw/upload/images/20220929/20108931bmeZyQWiBY.png

  ...
  
  @protected
  void setState(VoidCallback fn) {
    
	  ...
	  
	  final Object? result = fn() as dynamic;
	  
	  ...
    
	// 將element標記為dirty並將其添加到小部件的global list中,並在下一frame中重建
	  _element!.markNeedsBuild();
  }
  
  ...

我們來看一下markNeedsBuild方法,

  void markNeedsBuild() {
    assert(_lifecycleState != _ElementLifecycle.defunct);
    if (_lifecycleState != _ElementLifecycle.active)
      return;
	  
	  ...
	  
    if (dirty)
      return;
    _dirty = true;
    owner!.scheduleBuildFor(this);
  }

將Element的_dirty設為true。當該變量為true,在下一次Vsync來到時Element才會被重新build。並同時將該Element加入到owner中,

owner: 此Element的父Element(一個Element可能有多個子控件組成)

接著我們看到scheduleBuildFor

  /// Adds an element to the dirty elements list so that it will be rebuilt
  /// when [WidgetsBinding.drawFrame] calls [buildScope].
  void scheduleBuildFor(Element element) {
  
      if (element._inDirtyList) {
      
	    ...
	  
      _dirtyElementsNeedsResorting = true;
      return;
    }
    if (!_scheduledFlushDirtyElements && onBuildScheduled != null) {
      _scheduledFlushDirtyElements = true;
      onBuildScheduled!();
    }
    _dirtyElements.add(element);
    element._inDirtyList = true;
	
	  ...
	}

在父Element中將此Element標記為dirty,並且由於調用build是從父控件到子控件逐層調用的,每一個父控件藉由BuildOwner來管理它的子控件,接下來再調用onBuildScheduled()

setState()流程

總結思路一下,簡單的了解setState()的工作流程:

1.呼叫setState()
2.當前Widget標記為可以刷新(dirty)
3.遍歷子widget標記為可以刷新
4.等待下一個Vsync刷新信號
5.呼叫此widget及其子widget的build進行刷新

簡單的介紹一下工作原理後,現在我們知道setState為什麼能重繪widget,那麼該如何使用?

盡量保持widget小: 因為我們知道當她rebuild時,會讓他的子代也跟著被重建,所以使用時,最好不要讓太多的widget進行重建

使用注意[Source Link]:

  1. Keep your widgets small
  2. Don’t call setState in build methods
  3. Don’t call setState in initState methods
  4. setState() and setState(…) are equal
  5. setState(…) code must be small
  6. setState(…) code must not be async

我們今天簡單的了解setState如何運作後,明天我們來介紹InheritedWidget


上一篇
13.flutter的狀態管理(一)
下一篇
15.flutter的狀態管理(三)
系列文
30天學習flutter30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言